﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Program.cs" company="Microsoft Corporation">
//   Microsoft Visual Studio ALM Rangers
// </copyright>
// <summary>
//   The program.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Microsoft.ALMRangers.PermissionsExtractionTool
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Xml.Serialization;

    using CommandLine;
    using Microsoft.ALMRangers.PermissionsExtractionTool.Properties;
    using TeamFoundation.Client;
    using TeamFoundation.Framework.Client;
    using TeamFoundation.Framework.Common;
    using TeamFoundation.VersionControl.Client;
    using TeamFoundation.VersionControl.Common;
    using TeamFoundation.WorkItemTracking.Client;
    using XmlTransformation;

    /// <summary>
    /// The program.
    /// </summary>
    internal static class Program
    {
        //// Static Fields

        /// <summary>
        /// The area security namespace id.
        /// </summary>
        private static readonly Guid AreaSecurityNamespaceId = new Guid("83E28AD4-2D72-4CEB-97B0-C7726D5502C3");

        /// <summary>
        /// The build namespace id.
        /// </summary>
        private static readonly Guid BuildNamespaceId = new Guid("33344D9C-FC72-4D6F-ABA5-FA317101A7E9");

        /// <summary>
        /// The iteration security namespace id.
        /// </summary>
        private static readonly Guid IterationSecurityNamespaceId = new Guid("BF7BFA03-B2B7-47DB-8113-FA2E002CC5B1");

        /// <summary>
        /// The version control namespace id.
        /// </summary>
        private static readonly Guid VersionControlNamespaceId = new Guid("a39371cf-0841-4c16-bbd3-276e341bc052");
        private static readonly Guid GitVersionControlNamespaceId = new Guid("2E9EB7ED-3C0A-47D4-87C1-0FFDD275FD87");

        /// <summary>
        /// The warehouse namespace id.
        /// </summary>
        private static readonly Guid WarehouseNamespaceId = new Guid("B8FBAB8B-69C8-4CD9-98B5-873656788EFB");

        /// <summary>
        /// The work item namespace id.
        /// </summary>
        private static readonly Guid WorkItemNamespaceId = new Guid("71356614-AAD7-4757-8F2C-0FB3BFF6F680");

        /// <summary>
        /// The workspace security namespace id.
        /// </summary>
        private static readonly Guid WorkspaceSecurityNamespaceId = new Guid("93BAFC04-9075-403A-9367-B7164EAC6B5C");

        /// <summary>
        /// Sends a failure message of the ERROR output and wait for a press on the enter key.
        /// </summary>
        /// <param name="message">message about the failure</param>
        private static void Fail(string message = null)
        {
            if (!string.IsNullOrEmpty(message))
            {
                Console.Error.WriteLine(message);
            }

            Console.WriteLine("Press <Enter> to continue...");
            Console.ReadLine();
        }

        /// <summary>
        /// Console entry point
        /// </summary>
        /// <param name="args">command line arguments</param>
        /// <returns>0 if all extractions are successful.</returns>
        private static int Main(string[] args)
        {
            var options = new Options();

            var parser = new Parser(settings =>
            {
                settings.CaseSensitive = false;
                settings.HelpWriter = Console.Error;
                settings.ParsingCulture = CultureInfo.InvariantCulture;
            });

            var result = parser.ParseArguments(args, options);

            if (!result)
            {
                Fail();
                return -1;
            }

            TfsTeamProjectCollection tfs =
                TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(options.Collection));
            try
            {
                tfs.EnsureAuthenticated();
            }
            catch (Exception)
            {
                Fail("Connection to TFS failed");
                return -1;
            }

            // Getting Version Control, GroupService, Authorization Service, WorkItemStore and Common Structure Services
            var vcs = tfs.GetService<VersionControlServer>();

            // Getting Identity Service
            var ims = tfs.GetService<IIdentityManagementService>();

            var someExtractionFail = false;
            foreach (var userName in options.Users)
            {
                Console.WriteLine("===== Extracting Permissions for User {0} ======", userName);
                var fileName = GenerateFileName(userName, options.OutputFile);
                var extractionStatus = ExtractPermissionForUserName(ims, userName, options, fileName, tfs);

                if (!extractionStatus)
                {
                    someExtractionFail = true;
                }
            }

            if (someExtractionFail)
            {
                Fail("An error occured during the extraction");
                return -1;
            }

            return 0;
        }

        /// <summary>
        /// Insert the userName into the filename pattern
        /// </summary>
        /// <param name="userName">account name</param>
        /// <param name="fileName">filename pattern</param>
        /// <returns>Consolidated file name</returns>
        private static string GenerateFileName(string userName, string fileName)
        {
            var rootPart = Path.GetFileNameWithoutExtension(fileName);

            var userNamePart = Regex.Replace(userName, @"\W", "_");

            var result = Path.Combine(Path.GetDirectoryName(fileName), string.Format("{0}_{1}{2}", rootPart, userNamePart, Path.GetExtension(fileName)));

            return result;
        }

        /// <summary>
        /// Run the extraction algorithm for a specific user
        /// </summary>
        /// <param name="ims">Identity management service</param>
        /// <param name="userName">user name</param>
        /// <param name="options">command line parameters</param>
        /// <param name="fileName">File name</param>
        /// <param name="tfs">team project collection</param>
        /// <returns>true if successful</returns>
        private static bool ExtractPermissionForUserName(IIdentityManagementService ims, string userName, Options options, string fileName, TfsTeamProjectCollection tfs)
        {
            TeamFoundationIdentity userIdentity = ims.ReadIdentity(
                IdentitySearchFactor.AccountName,
                userName,
                MembershipQuery.None,
                ReadIdentityOptions.IncludeReadFromSource);

            if (userIdentity == null)
            {
                Console.WriteLine(string.Format("User {0} can't connect to the Collection {1}. Please verifiy!", userName, options.Collection));
                Console.ReadLine();
                return false;
            }

            // get workItem store
            var workItemStore = tfs.GetService<WorkItemStore>();

            ////Initiate Report

            // Initiate a new object of Permission Report
            var permissionsReport = new PermissionsReport
            {
                Date = DateTime.Now,
                TfsCollectionUrl = options.Collection,
                UserName = userName,
                TeamProjects = new List<TfsTeamProject>()
            };

            // reterive list of Team Projects in the give collection
            ProjectCollection workItemsProjects = workItemStore.Projects;
            foreach (Project teamProject in workItemsProjects)
            {
                // Create project security token
                string projectSecurityToken = "$PROJECT:" + teamProject.Uri.AbsoluteUri;

                /////Project Permissions

                // Project Permissions   
                var server = tfs.GetService<ISecurityService>();
                var vcs = tfs.GetService<VersionControlServer>();
                TeamFoundation.Git.Client.GitRepositoryService gitRepostoryService = tfs.GetService<TeamFoundation.Git.Client.GitRepositoryService>();

                Console.WriteLine("==== Extracting Permissions for {0} Project ====", teamProject.Name);
                var groups = GetUserGroups(tfs, teamProject.Uri.AbsoluteUri, userIdentity);
                var projectLevelPermissions = ExtractProjectPermissions(server, groups, userIdentity, projectSecurityToken, ims);

                ////Version Control Permissions
                var versionControlPermissions = ExtractVersionControlPermissions(server, groups, userIdentity, teamProject.Name, ims, vcs);
                var gitVersionControlPermissions = ExtractGitVersionControlPermissions(server, groups, userIdentity, teamProject.Name, ims, vcs, gitRepostoryService);

                ////Build Permissions
                var buildPermissions = ExtractBuildPermissions(server, projectSecurityToken, userIdentity);

                ////Warehouse Permissions
                var warehousePermissions = ExtractWarehousePermissions(server, projectSecurityToken, userIdentity);

                ////WorkItems Permissions
                var workItemsPermissions = ExtractWorkItemsPermissions(server, projectSecurityToken, userIdentity);

                ////WorkItems Area Permissions
                var areasPermissions = ExtractAreasPermissions(server, teamProject, userIdentity);

                var iterationPermissions = new List<IterationPermission>();
                ////WorkItems Iteration Permissions
                var workItemsIterationSecurityNamespace = ExtractIterationPermissions(server, teamProject, userIdentity, ref iterationPermissions);

                // Workspace Permissions
                var workspacePermission = ExtractWorkspacePermissions(server, workItemsIterationSecurityNamespace, projectSecurityToken, userIdentity);

                // Set TFS report Data
                // Create Team Project node in XML file
                var tfsTeamProject = new TfsTeamProject
                {
                    Name = teamProject.Name,
                    AreaPermissions = areasPermissions,
                    BuildPermissions = buildPermissions,
                    IterationPermissions = iterationPermissions,
                    ProjectLevelPermissions =
                        new ProjectLevelPermissions
                        {
                            ProjectLevelPermissionsList
                                =
                                projectLevelPermissions
                        }
                };
                tfsTeamProject.GitVersionControlPermissions = new VersionControlPermissions
                {
                    VersionControlPermissionsList = gitVersionControlPermissions
                };
                tfsTeamProject.VersionControlPermissions = new VersionControlPermissions
                {
                    VersionControlPermissionsList = versionControlPermissions
                };

                tfsTeamProject.VersionControlPermissions.VersionControlPermissionsList.AddRange(workItemsPermissions);
                tfsTeamProject.WorkItemsPermissions = workItemsPermissions;
                permissionsReport.TeamProjects.Add(tfsTeamProject);
            }

            ////Generate output file
            var fs = new FileStream(fileName, FileMode.Create);
            var streamWriter = new StreamWriter(fs, Encoding.UTF8);

            using (streamWriter)
            {
                var xmlSerializer = new XmlSerializer(typeof(PermissionsReport));
                xmlSerializer.Serialize(streamWriter, permissionsReport);
                streamWriter.Flush();
            }

            if (options.Html)
            {
                var tranformationFileName = Path.Combine(Path.GetDirectoryName(fileName), "ALMRanger.xsl");
                File.WriteAllText(tranformationFileName, Resources.ALMRangers_SampleXslt);
                var htmlOuput = Path.ChangeExtension(fileName, ".html");

                var logoFile = Path.Combine(Path.GetDirectoryName(fileName), "ALMRangers_Logo.png");
                Resources.ALMRangers_Logo.Save(logoFile);
                XmlTransformationManager.TransformXmlUsingXsl(fileName, tranformationFileName, htmlOuput);
            }

            return true;
        }

        /// <summary>
        /// returns the project permissions
        /// </summary>
        /// <param name="server">TFS Security service</param>
        /// <param name="groups">Security groups</param>
        /// <param name="userIdentity">User identity of the current extraction</param>
        /// <param name="projectSecurityToken">The project security token</param>
        /// <param name="ims"> TFS Identity service</param>
        /// <returns>The permission list</returns>
        private static List<Permission> ExtractProjectPermissions(ISecurityService server, List<string> groups, TeamFoundationIdentity userIdentity, string projectSecurityToken, IIdentityManagementService ims)
        {
            SecurityNamespace projectSecurityNamespace =
                   server.GetSecurityNamespace(FrameworkSecurity.TeamProjectNamespaceId);
            AccessControlList projectAccessList =
                projectSecurityNamespace.QueryAccessControlList(
                    projectSecurityToken,
                    new List<IdentityDescriptor> { userIdentity.Descriptor },
                    true);
            var result = new List<Permission>();
            Console.WriteLine("== Exracting Project Permissions ==");

            foreach (AccessControlEntry ace in projectAccessList.AccessControlEntries)
            {
                if (0 != ace.Allow)
                {
                    var allowedPermissions = ((EnumrationsList.AuthorizationProjectPermissions)ace.Allow).ToString();
                    result.AddRange(
                        Helpers.GetActionDetailsByName(allowedPermissions, "Allow", PermissionScope.TeamProject));
                }

                if (0 != ace.Deny)
                {
                    var denyPermissions = ((EnumrationsList.AuthorizationProjectPermissions)ace.Deny).ToString();
                    result.AddRange(
                        Helpers.GetActionDetailsByName(denyPermissions, "Deny", PermissionScope.TeamProject));
                }
            }

            foreach (string group in groups)
            {
                TeamFoundationIdentity groupIdentity = ims.ReadIdentity(IdentitySearchFactor.Identifier, group, MembershipQuery.None, ReadIdentityOptions.IncludeReadFromSource);

                AccessControlList groupAccessList =
                   projectSecurityNamespace.QueryAccessControlList(
                       projectSecurityToken,
                       new List<IdentityDescriptor> { groupIdentity.Descriptor },
                       true);

                foreach (AccessControlEntry ace in groupAccessList.AccessControlEntries)
                {
                    if (0 != ace.Allow)
                    {
                        var allowedPermissions = ((EnumrationsList.AuthorizationProjectPermissions)ace.Allow).ToString();
                        var permissionsList = Helpers.GetActionDetailsByName(allowedPermissions, "Inherited Allow", PermissionScope.TeamProject);
                        AppendGroupInheritanceInformation(permissionsList, groupIdentity.DisplayName);
                        result.AddRange(permissionsList);
                    }

                    if (0 != ace.Deny)
                    {
                        var denyPermissions = ((EnumrationsList.AuthorizationProjectPermissions)ace.Deny).ToString();
                        var permissionsList = Helpers.GetActionDetailsByName(denyPermissions, "Inherited Deny", PermissionScope.TeamProject);
                        AppendGroupInheritanceInformation(permissionsList, groupIdentity.DisplayName);
                        result.AddRange(permissionsList);
                    }
                }
            }

            var modifiedPermissions = RemoveDuplicatePermissionsAndCombineGroups(result);
            return modifiedPermissions;
        }

        /// <summary>
        /// Removes the duplicate permissions in the list and combine them if it is possible
        /// </summary>
        /// <param name="permissions">Permission list to process</param>
        /// <returns>A simplified permission list</returns>
        private static List<Permission> RemoveDuplicatePermissionsAndCombineGroups(List<Permission> permissions)
        {
            List<Permission> permissionsList = new List<Permission>();

            foreach (var permission in permissions)
            {
                var duplicatePermissions = (from dupPermission in permissions
                                            where dupPermission.InternalName == permission.InternalName
                                            select dupPermission).ToList();
                if (duplicatePermissions.Count > 1)
                {
                    // Get inhirted group names
                    string[] groupNames = new string[duplicatePermissions.Count];
                    int counter = 0;
                    foreach (var item in duplicatePermissions)
                    {
                        groupNames[counter] = item.GroupMemberInheritance;
                        counter++;
                    }
                    
                    Permission modifiedPermission = duplicatePermissions[0];
                    if (!permissionsList.Exists(x => x.PermissionConstant == modifiedPermission.PermissionConstant))
                    {
                        modifiedPermission.GroupMemberInheritance = string.Join(",", groupNames);

                        // Add only one Item
                        permissionsList.Add(modifiedPermission);
                    }
                }
                else
                {
                    permissionsList.Add(permission);
                }
            }
            
            return permissionsList;
        }

        /// <summary>
        /// Add inheritance info in the permission list
        /// </summary>
        /// <param name="permissions">Permission list to alter</param>
        /// <param name="groupName">Group inheritance to add</param>
        private static void AppendGroupInheritanceInformation(IEnumerable<Permission> permissions, string groupName)
        {
            foreach (Permission permission in permissions)
            {
                permission.GroupMemberInheritance = groupName;
            }
        }

        /// <summary>
        /// Extracts workspace permission for a specific identity
        /// </summary>
        /// <param name="server">Security Service</param>
        /// <param name="workItemsIterationSecurityNamespace">WorkItemsIteration Security Namespace</param>
        /// <param name="projectSecurityToken">Security Token</param>
        /// <param name="userIdentity">User Identity</param>
        /// <returns>The list of the permissions</returns>
        private static List<Permission> ExtractWorkspacePermissions(
            ISecurityService server,
            SecurityNamespace workItemsIterationSecurityNamespace,
            string projectSecurityToken,
            TeamFoundationIdentity userIdentity)
        {
            var result = new List<Permission>();
            SecurityNamespace workItemsWorkspaceSecurityNamespace = server.GetSecurityNamespace(WorkspaceSecurityNamespaceId);
            if (workItemsWorkspaceSecurityNamespace != null)
            {
                Console.WriteLine("== Workspace Permissions ==");
                AccessControlList workspaceAccessList =
                    workItemsIterationSecurityNamespace.QueryAccessControlList(
                        projectSecurityToken,
                        new List<IdentityDescriptor> { userIdentity.Descriptor },
                        true);
                string allowedWorkspacePermissions = string.Empty;
                string denyWorkspacePermissions = string.Empty;
                foreach (AccessControlEntry ace in workspaceAccessList.AccessControlEntries)
                {
                    if (0 != ace.Allow)
                    {
                        allowedWorkspacePermissions = ((VersionedItemPermissions)ace.Allow).ToString();
                        result.AddRange(
                            Helpers.GetActionDetailsByName(allowedWorkspacePermissions, "Allow", PermissionScope.SourceControl));
                    }

                    if (0 != ace.Deny)
                    {
                        denyWorkspacePermissions = ((EnumrationsList.AuthorizationIterationNodePermissions)ace.Deny).ToString();
                        result.AddRange(
                            Helpers.GetActionDetailsByName(denyWorkspacePermissions, "Deny", PermissionScope.SourceControl));
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Returns a flat list of all the nodes in the input collection.
        /// </summary>
        /// <param name="nodes">input node list</param>
        /// <returns> An iterator on the flat node list</returns>
        private static IEnumerable<Node> FlattenTree(NodeCollection nodes)
        {
            foreach (Node node in nodes)
            {
                yield return node;
                if (node.HasChildNodes)
                {
                    foreach (var childNode in FlattenTree(node.ChildNodes))
                    {
                        yield return childNode;
                    }
                }
            }
        }

        /// <summary>
        /// Extract iteration permissions for a specific identity
        /// </summary>
        /// <param name="server">Server security service</param>
        /// <param name="teamProject">Team project</param>
        /// <param name="userIdentity">User identity</param>
        /// <param name="iterationPermissions">The list of the extracted permissions</param> 
        /// <returns>Security namespace</returns>
        private static SecurityNamespace ExtractIterationPermissions(ISecurityService server, Project teamProject, TeamFoundationIdentity userIdentity, ref List<IterationPermission> iterationPermissions)
        {
            SecurityNamespace workItemsIterationSecurityNamespace = server.GetSecurityNamespace(IterationSecurityNamespaceId);
            if (workItemsIterationSecurityNamespace != null)
            {
                var lstIterations = FlattenTree(teamProject.IterationRootNodes);

                Console.WriteLine("== WorkItems Iteration Permissions ==");
                foreach (Node iteration in lstIterations)
                {
                    var iterationPermission = new IterationPermission
                                                  {
                                                      IterationName = iteration.Path,
                                                      IterationPermissions = new List<Permission>()
                                                  };

                    // Get Iteration token
                    string iterationSecurityToken = iteration.Uri.AbsoluteUri;
                    AccessControlList workItemsIterationAccessList =
                        workItemsIterationSecurityNamespace.QueryAccessControlList(
                            iterationSecurityToken,
                            new List<IdentityDescriptor> { userIdentity.Descriptor },
                            true);

                    foreach (AccessControlEntry ace in workItemsIterationAccessList.AccessControlEntries)
                    {
                        if (0 != ace.Allow)
                        {
                            var allowedWorkItemIterationPermissions =
                                ((EnumrationsList.AuthorizationIterationNodePermissions)ace.Allow).ToString();
                            iterationPermission.IterationPermissions.AddRange(
                                Helpers.GetActionDetailsByName(
                                    allowedWorkItemIterationPermissions,
                                    "Allow",
                                    PermissionScope.WorkItemIterations));
                        }

                        if (0 != ace.Deny)
                        {
                            var denyWorkItemIterationPermissions =
                                ((EnumrationsList.AuthorizationIterationNodePermissions)ace.Deny).ToString();
                            iterationPermission.IterationPermissions.AddRange(
                                Helpers.GetActionDetailsByName(
                                    denyWorkItemIterationPermissions,
                                    "Deny",
                                    PermissionScope.WorkItemIterations));
                        }
                    }

                    if (iterationPermission.IterationPermissions.Count > 0)
                    {
                        iterationPermissions.Add(iterationPermission);
                    }
                }
            }

            return workItemsIterationSecurityNamespace;
        }

        /// <summary>
        /// Extracts area permission for a specific identity
        /// </summary>
        /// <param name="server">Server security service</param>
        /// <param name="teamProject">Team project</param>
        /// <param name="userIdentity">User identity</param>
        /// /// <returns>The list of the permissions</returns>
        private static List<AreaPermission> ExtractAreasPermissions(
            ISecurityService server,
            Project teamProject,
            TeamFoundationIdentity userIdentity)
        {
            var result = new List<AreaPermission>();
            SecurityNamespace workItemsAreaSecurityNamespace = server.GetSecurityNamespace(AreaSecurityNamespaceId);
            if (workItemsAreaSecurityNamespace != null)
            {
                Console.WriteLine("== WorkItems Area Permissions ==");
                IEnumerable<Node> areaList = FlattenTree(teamProject.AreaRootNodes);
                foreach (Node area in areaList)
                {
                    var areaPermission = new AreaPermission { AreaName = area.Path, AreaPermissions = new List<Permission>() };

                    ////Get Iteration token
                    string areaSecurityToken = area.Uri.AbsoluteUri;
                    AccessControlList workItemsAreaAccessList =
                        workItemsAreaSecurityNamespace.QueryAccessControlList(
                            areaSecurityToken,
                            new List<IdentityDescriptor> { userIdentity.Descriptor },
                            true);
                    string allowedWorkItemAreaPermissions = string.Empty;
                    string denyWorkItemAreaPermissions = string.Empty;
                    foreach (AccessControlEntry ace in workItemsAreaAccessList.AccessControlEntries)
                    {
                        if (0 != ace.Allow)
                        {
                            allowedWorkItemAreaPermissions =
                                ((EnumrationsList.AuthorizationCssNodePermissions)ace.Allow).ToString();
                            areaPermission.AreaPermissions.AddRange(
                                Helpers.GetActionDetailsByName(
                                    allowedWorkItemAreaPermissions,
                                    "Allow",
                                    PermissionScope.WorkItemAreas));
                        }

                        if (0 != ace.Deny)
                        {
                            denyWorkItemAreaPermissions = ((EnumrationsList.AuthorizationCssNodePermissions)ace.Deny).ToString();
                            areaPermission.AreaPermissions.AddRange(
                                Helpers.GetActionDetailsByName(
                                    denyWorkItemAreaPermissions,
                                    "Deny",
                                    PermissionScope.WorkItemAreas));
                        }
                    }

                    if (areaPermission.AreaPermissions.Count > 0)
                    {
                        result.Add(areaPermission);
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Extracts work item permissions for a specific identity
        /// </summary>
        /// <param name="server">Server security service</param>
        /// <param name="projectSecurityToken">Project Security Token</param>
        /// <param name="userIdentity">User identity</param>
        /// <returns>The list of the permissions</returns>
        private static List<Permission> ExtractWorkItemsPermissions(
            ISecurityService server,
            string projectSecurityToken,
            TeamFoundationIdentity userIdentity)
        {
            var result = new List<Permission>();
            SecurityNamespace workItemsSecurityNamespace = server.GetSecurityNamespace(WorkItemNamespaceId);
            if (workItemsSecurityNamespace != null)
            {
                Console.WriteLine("== WorkItems Permissions ==");
                AccessControlList workItemsAccessList = workItemsSecurityNamespace.QueryAccessControlList(
                    projectSecurityToken,
                    new List<IdentityDescriptor> { userIdentity.Descriptor },
                    true);
                string allowedWorkItemPermissions = string.Empty;
                string denyWorkItemPermissions = string.Empty;
                foreach (AccessControlEntry ace in workItemsAccessList.AccessControlEntries)
                {
                    if (0 != ace.Allow)
                    {
                        allowedWorkItemPermissions = ((QueryItemPermissions)ace.Allow).ToString();
                        result.AddRange(
                            Helpers.GetActionDetailsByName(allowedWorkItemPermissions, "Allow", PermissionScope.WorkItemAreas));
                    }

                    if (0 != ace.Deny)
                    {
                        denyWorkItemPermissions = ((QueryItemPermissions)ace.Deny).ToString();
                        result.AddRange(
                            Helpers.GetActionDetailsByName(denyWorkItemPermissions, "Deny", PermissionScope.WorkItemAreas));
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Extract warehouse permission for a specific identity
        /// </summary>
        /// <param name="server">Server security service</param>
        /// <param name="projectSecurityToken">Project Security Token</param>
        /// <param name="userIdentity">User identity</param>
        /// <returns>The list of the permissions</returns>
        private static List<Permission> ExtractWarehousePermissions(
            ISecurityService server,
            string projectSecurityToken,
            TeamFoundationIdentity userIdentity)
        {
            var result = new List<Permission>();
            SecurityNamespace warehouseAdministrationSecurityNamespace = server.GetSecurityNamespace(WarehouseNamespaceId);
            if (warehouseAdministrationSecurityNamespace != null)
            {
                Console.WriteLine("== Warehouse Permissions ==");
                AccessControlList warehouseAccessList =
                    warehouseAdministrationSecurityNamespace.QueryAccessControlList(
                        projectSecurityToken,
                        new List<IdentityDescriptor> { userIdentity.Descriptor },
                        true);

                foreach (AccessControlEntry ace in warehouseAccessList.AccessControlEntries)
                {
                    if (0 != ace.Allow)
                    {
                        result.Add(new Permission() { Value = "Allow" });
                        var allowedWarehousePermissions = "    Allow: " + ((EnumrationsList.WarehousePermissions)ace.Allow);
                    }

                    if (0 != ace.Deny)
                    {
                        result.Add(new Permission() { Value = "Deny" });
                        var denyWarehousePermissions = "       Deny: " + ((EnumrationsList.WarehousePermissions)ace.Deny);
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Extracts build permissions for a specific identity.
        /// </summary>
        /// <param name="server">Server security service</param>
        /// <param name="projectSecurityToken">Project Security Token</param>
        /// <param name="userIdentity">User identity</param>
        /// <returns>The list of the permissions</returns>
        private static List<Permission> ExtractBuildPermissions(
            ISecurityService server,
            string projectSecurityToken,
            TeamFoundationIdentity userIdentity)
        {
            var result = new List<Permission>();
            SecurityNamespace buildSecurityNamespace = server.GetSecurityNamespace(BuildNamespaceId);
            Console.WriteLine("== Extracting Build Permissions ==");
            AccessControlList buildAccessList = buildSecurityNamespace.QueryAccessControlList(
                projectSecurityToken,
                new List<IdentityDescriptor> { userIdentity.Descriptor },
                true);

            foreach (AccessControlEntry ace in buildAccessList.AccessControlEntries)
            {
                if (0 != ace.Allow)
                {
                    var allowedBuildPermissions = ((EnumrationsList.BuildPermissions)ace.Allow).ToString();
                    result.AddRange(
                        Helpers.GetActionDetailsByName(allowedBuildPermissions, "Allow", PermissionScope.TeamBuild));
                }

                if (0 != ace.Deny)
                {
                    var denyBuildPermissions = ((EnumrationsList.BuildPermissions)ace.Deny).ToString();
                    result.AddRange(
                        Helpers.GetActionDetailsByName(denyBuildPermissions, "Deny", PermissionScope.TeamBuild));
                }
            }

            return result;
        }

        /// <summary>
        /// Extracts version control permissions for a specific identity.
        /// </summary>
        /// <param name="server">TFS Security service</param>
        /// <param name="groups">Security groups</param>
        /// <param name="userIdentity">User Identity</param>
        /// <param name="projectSecurityToken">Project Security Token</param>
        /// <param name="ims">TFS Identity service</param>
        /// <param name="vcs">TFS VC Service</param>
        /// <returns>The list of the permissions</returns>
        private static List<Permission> ExtractVersionControlPermissions(
            ISecurityService server,
            List<string> groups,
            TeamFoundationIdentity userIdentity,
            string projectSecurityToken,
            IIdentityManagementService ims,
            VersionControlServer vcs)
        {
            Console.WriteLine("== Extract Version Control Permissions ==");
            SecurityNamespace versionControlSecurityNamespace =
                   server.GetSecurityNamespace(VersionControlNamespaceId);
            var teamProject = vcs.TryGetTeamProject(projectSecurityToken);
            if (teamProject == null)
            {
                return new List<Permission>();
            }

            // vcs.GetTeamProject(projectSecurityToken);
            AccessControlList versionControlAccessList =
                versionControlSecurityNamespace.QueryAccessControlList(
                    teamProject.ServerItem,
                    new List<IdentityDescriptor> { userIdentity.Descriptor },
                    true);
            var versionControlPermissions = new List<Permission>();

            foreach (AccessControlEntry ace in versionControlAccessList.AccessControlEntries)
            {
                if (0 != ace.Allow)
                {
                    var allowedVersionControlPermissions = ((VersionedItemPermissions)ace.Allow).ToString();
                    versionControlPermissions.AddRange(
                        Helpers.GetActionDetailsByName(allowedVersionControlPermissions, "Allow", PermissionScope.SourceControl));
                }

                if (0 != ace.Deny)
                {
                    var denyVersionControlPermissions = ((VersionedItemPermissions)ace.Deny).ToString();
                    versionControlPermissions.AddRange(
                        Helpers.GetActionDetailsByName(denyVersionControlPermissions, "Deny", PermissionScope.SourceControl));
                }
            }

            foreach (string group in groups)
            {
                TeamFoundationIdentity groupIdentity = ims.ReadIdentity(IdentitySearchFactor.Identifier, group, MembershipQuery.None, ReadIdentityOptions.IncludeReadFromSource);

                AccessControlList groupAccessList =
              versionControlSecurityNamespace.QueryAccessControlList(
                  teamProject.ServerItem,
                  new List<IdentityDescriptor> { groupIdentity.Descriptor },
                  true);

                foreach (AccessControlEntry ace in groupAccessList.AccessControlEntries)
                {
                    if (0 != ace.Allow)
                    {
                        var allowedPermissions = ((VersionedItemPermissions)ace.Allow).ToString();
                        var permissionsList = Helpers.GetActionDetailsByName(allowedPermissions, "Inherited Allow", PermissionScope.SourceControl);
                        AppendGroupInheritanceInformation(permissionsList, groupIdentity.DisplayName);
                        versionControlPermissions.AddRange(permissionsList);
                    }

                    if (0 != ace.Deny)
                    {
                        var denyPermissions = ((VersionedItemPermissions)ace.Deny).ToString();
                        var permissionsList = Helpers.GetActionDetailsByName(denyPermissions, "Inherited Deny", PermissionScope.SourceControl);
                        AppendGroupInheritanceInformation(permissionsList, groupIdentity.DisplayName);
                        versionControlPermissions.AddRange(permissionsList);
                    }
                }
            }

            var modifiedPermissions = RemoveDuplicatePermissionsAndCombineGroups(versionControlPermissions);
            return modifiedPermissions;
        }

        /// <summary>
        /// Extracts the git version control permissions.
        /// </summary>
        /// <param name="server">The server.</param>
        /// <param name="groups">The groups.</param>
        /// <param name="userIdentity">The user identity.</param>
        /// <param name="projectSecurityToken">The project security token.</param>
        /// <param name="ims">The ims.</param>
        /// <param name="vcs">The VCS.</param>
        /// <param name="gitService">The git service.</param>
        /// <returns></returns>
        private static List<Permission> ExtractGitVersionControlPermissions(
           ISecurityService server,
           List<string> groups,
           TeamFoundationIdentity userIdentity,
           string projectSecurityToken,
           IIdentityManagementService ims,
           VersionControlServer vcs,
            TeamFoundation.Git.Client.GitRepositoryService gitService)
        {
            Console.WriteLine("== Extract Git Version Control Permissions ==");
            SecurityNamespace gitVersionControlSecurityNamespace =
                   server.GetSecurityNamespace(GitVersionControlNamespaceId);
           var gitProjectRepoService=  gitService.QueryRepositories(projectSecurityToken);
            //This sample handle only the default repository, you can iterate through all repositories same way
            var defaultGitRepo = gitProjectRepoService.SingleOrDefault(gr => gr.Name.Equals(projectSecurityToken));
          
            var teamProject = vcs.TryGetTeamProject(projectSecurityToken);
            if (defaultGitRepo == null)
            {
                return new List<Permission>();
            }
            //Repository Security Token is repoV2/TeamProjectId/RepositoryId
            var repoIdToken = string.Format("repoV2{0}{1}{2}{3}", gitVersionControlSecurityNamespace.Description.SeparatorValue,defaultGitRepo.ProjectReference.Id, gitVersionControlSecurityNamespace.Description.SeparatorValue, defaultGitRepo.Id);
            // vcs.GetTeamProject(projectSecurityToken);
            AccessControlList versionControlAccessList =
                gitVersionControlSecurityNamespace.QueryAccessControlList(
                    repoIdToken,
                    new List<IdentityDescriptor> { userIdentity.Descriptor },
                    true);
            var gitVersionControlPermissions = new List<Permission>();

            foreach (AccessControlEntry ace in versionControlAccessList.AccessControlEntries)
            {
                if (0 != ace.Allow)
                {
                    var allowedVersionControlPermissions = ((EnumrationsList.GitPermissions)ace.Allow).ToString();
                    gitVersionControlPermissions.AddRange(
                        Helpers.GetActionDetailsByName(allowedVersionControlPermissions, "Allow", PermissionScope.GitSourceControl));
                }

                if (0 != ace.Deny)
                {
                    var denyVersionControlPermissions = ((EnumrationsList.GitPermissions)ace.Deny).ToString();
                    gitVersionControlPermissions.AddRange(
                        Helpers.GetActionDetailsByName(denyVersionControlPermissions, "Deny", PermissionScope.GitSourceControl));
                }
            }
            if(gitVersionControlPermissions.Count ==0)
            {
                foreach (AccessControlEntry ace in versionControlAccessList.AccessControlEntries)
                {
                    if (0 != ace.ExtendedInfo.EffectiveAllow)
                    {
                        var allowedVersionControlPermissions = ((EnumrationsList.GitPermissions)ace.ExtendedInfo.EffectiveAllow).ToString();
                        gitVersionControlPermissions.AddRange(
                            Helpers.GetActionDetailsByName(allowedVersionControlPermissions, "Allow", PermissionScope.GitSourceControl));
                    }

                    if (0 != ace.ExtendedInfo.EffectiveDeny)
                    {
                        var denyVersionControlPermissions = ((EnumrationsList.GitPermissions)ace.ExtendedInfo.EffectiveDeny).ToString();
                        gitVersionControlPermissions.AddRange(
                            Helpers.GetActionDetailsByName(denyVersionControlPermissions, "Deny", PermissionScope.GitSourceControl));
                    }
                }
            }

            foreach (string group in groups)
            {
                TeamFoundationIdentity groupIdentity = ims.ReadIdentity(IdentitySearchFactor.Identifier, group, MembershipQuery.None, ReadIdentityOptions.IncludeReadFromSource);

                AccessControlList groupAccessList =
              gitVersionControlSecurityNamespace.QueryAccessControlList(
               repoIdToken,
                  new List<IdentityDescriptor> { groupIdentity.Descriptor },
                  true);

                foreach (AccessControlEntry ace in groupAccessList.AccessControlEntries)
                {
                    if (0 != ace.Allow)
                    {
                        var allowedPermissions = ((EnumrationsList.GitPermissions)ace.Allow).ToString();
                        var permissionsList = Helpers.GetActionDetailsByName(allowedPermissions, "Inherited Allow", PermissionScope.GitSourceControl);
                        AppendGroupInheritanceInformation(permissionsList, groupIdentity.DisplayName);
                        gitVersionControlPermissions.AddRange(permissionsList);
                    }

                    if (0 != ace.Deny)
                    {
                        var denyPermissions = ((EnumrationsList.GitPermissions)ace.Deny).ToString();
                        var permissionsList = Helpers.GetActionDetailsByName(denyPermissions, "Inherited Deny", PermissionScope.GitSourceControl);
                        AppendGroupInheritanceInformation(permissionsList, groupIdentity.DisplayName);
                        gitVersionControlPermissions.AddRange(permissionsList);
                    }
                }
            }

            var modifiedPermissions = RemoveDuplicatePermissionsAndCombineGroups(gitVersionControlPermissions);
            return modifiedPermissions;
        }
        /// <summary>
        /// Returns the security group of the user
        /// </summary>
        /// <param name="tpc">Team project collection</param>
        /// <param name="teamProjectUri">Team Project Uri</param>
        /// <param name="userIdentity">User identity</param>
        /// <returns>The groups where the user is present.</returns>
        private static List<string> GetUserGroups(TfsTeamProjectCollection tpc, string teamProjectUri, TeamFoundationIdentity userIdentity)
        {
            List<string> groups = new List<string>();

            // Get the group security service.
            var gss = tpc.GetService<Microsoft.TeamFoundation.Server.IGroupSecurityService2>();

            Microsoft.TeamFoundation.Server.Identity[] appGroups = gss.ListApplicationGroups(teamProjectUri);

            foreach (Microsoft.TeamFoundation.Server.Identity group in appGroups)
            {
                Microsoft.TeamFoundation.Server.Identity[] groupMembers = gss.ReadIdentities(Microsoft.TeamFoundation.Server.SearchFactor.Sid, new string[] { group.Sid }, Microsoft.TeamFoundation.Server.QueryMembership.Expanded);
                foreach (Microsoft.TeamFoundation.Server.Identity member in groupMembers)
                {
                    if (member.Members != null)
                    {
                        foreach (string memberSid in member.Members)
                        {
                            if (memberSid.ToString() == userIdentity.Descriptor.Identifier)
                            {
                                groups.Add(group.Sid);
                            }
                        }
                    }
                }
            }

            return groups;
        }
    }
}
